// 递归[线性递归求和sum(4000000)、树状递归fibonacci(100)]可能出现爆栈【 RangeError: Maximum call stack size exceed】
// 0、console.trace()查看函数调用栈的栈帧数据
// 1、尾调用：无需返回的函数调用，即函数调用不处于参数位置上，写成尾调用形式有助于编译器对函数调用进行优化；
// 2、尾递归：写成尾调用形式的递归函数，如果编译器不做尾递归优化，也会爆栈[注：尾递归和循环等价]
// 3、尾递归优化PTC：编译器处理
// 4、CPS：Continuation Passing Style，continuation是程序控制流的抽象，表示后面将要进行的计算步骤（简写为kont函数）
// 5、Trampoline技法：尾调用函数层层嵌套，永不返回，然而在缺乏尾调用优化的语言中，并不知晓函数不会返回，状态、参数压栈依旧会发生，因此需要手动强制弹出下一层调用的函数，禁止解释器的压栈行为

// 参考：https://www.cnblogs.com/cheukyin/p/6444860.html、http://www.ruanyifeng.com/blog/2015/04/tail-call.html
'use strict';
const sumByRecursive = x => x <= 0 ? 0 : x + sumByRecursive(x - 1);// 递归函数实现，每调一次sumByRecursive，就新增一个执行环境ec ==> x过大会导致爆栈
const sumByLoop = x => { let r = 0; while (x > 0) { r += x; x--; } return r; };// 循环函数实现，只有一个执行环境ec ==> 不会爆栈
const sumByValRecursive = (x, v = 0) => x <= 0 ? v : sumByValRecursive(x - 1, x + v);// 尾递归实现，tail默认值
const sumByKontRecursive = (x, kont = x => x) => x <= 0 ? kont(0) : sumByKontRecursive(x - 1, res => kont(x + res));// 尾递归实现，kont默认处理函数

const sumByValTrampoline = (x, v = 0) => x <= 0 ? v : sumByValTrampoline.bind(null, x - 1, x + v);// 返回的是一个函数或默认值
const sumByKontTrampoline = (x, kont = x => { return { v: x, kont: null }; }) => { return { v: x, kont: x <= 0 ? kont : r => sumByKontTrampoline(x - 1, r => { return { kont: kont, v: r + x }; }) }; };// 返回的是一个对象

const trampolineKont = f => { while (typeof f === 'function') { f = f(); } return f; };// Trampoline技法：参数是一个函数，返回值不能是一个函数
const trampolineKontVal = o => { while (o.kont) { o = o.kont(o.v); } return o.v; };// Trampoline技法：参数是一个对象，返回值可以是一个函数

// 调用是否能够不爆栈正常运行，✓正常、✗爆栈
// sumByRecursive(4000000);// ✗
// sumByLoop(4000000);// ✓
// sumByValRecursive(4000000);// ✗
// sumByKontRecursive(4000000);// ✗
// trampolineKont(sumByValTrampoline(4000000));// ✓
// trampolineKontVal(sumByKontTrampoline(4000000));// ✓

// trampolineKontVal(sumByKontTrampoline(4000000, x => { return { v: pre => pre + ' result=' + x, kont: null }; }))('0~4000000的累加');
